/**
 * Copyright (c) 2005-20101 springside.org.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 
 * $Id: SimpleHibernateDao.java,v 1.1 2011/07/26 06:13:04 linzx Exp $
 */
package com.newy.core.hibernate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.annotation.Resource;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.newy.core.util.AssertUtils;
import com.newy.core.util.LangUtils;
import com.newy.core.util.ReflectionUtils;


/**
 * 封装Hibernate原生API的DAO泛型基类.
 * 
 * 参考Spring2.5自带的Petlinc例子, 取消了HibernateTemplate, 直接使用Hibernate原生API.
 * 
 * @param <T> DAO操作的对象类型
 * @param <ID> 主键类型
 * 
 * 代码修改自springside
 */

@SuppressWarnings("unchecked")
public class SimpleHibernateDao<T, ID extends Serializable> {

	protected Logger logger = LoggerFactory.getLogger(getClass());
	@Resource protected SessionFactory sessionFactory;
	protected Class<T> entityClass;

	//几个常用的排序对象
	protected Order ascIdOrder = Order.asc("id");
	protected Order descIdOrder = Order.desc("id");
	/** 按sortNum字段升序排序 */
	protected Order ascSortOrder = Order.asc("sortNum");
	/** 按sortNum字段降序排序 */
	protected Order descSortOrder = Order.desc("sortNum");
	
	/**
	 * 通过子类的泛型定义取得对象类型Class.
	 * eg.
	 * public class UserDao extends SimpleHibernateDao<User, Long>
	 */
	public SimpleHibernateDao() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}

	public SimpleHibernateDao(Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	/**
	 * 取得当前Session.
	 */
	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	protected Criteria createCriteria(){
		return this.getSession().createCriteria(this.entityClass);
	}
	
	/**
	 * 保存新增或修改的对象.
	 */
	public void save(final T entity) {
		AssertUtils.notNull(entity, "entity不能为空");
		getSession().saveOrUpdate(entity);
		logger.debug("save entity: {}", entity);
	}

	/**
	 * 删除对象.
	 * 
	 * @param entity 对象必须是session中的对象或含id属性的transient对象.
	 */
	public void delete(final T entity) {
		AssertUtils.notNull(entity, "entity不能为空");
		getSession().delete(entity);
		logger.debug("delete entity: {}", entity);
	}

	/**
	 * 按id删除对象.
	 */
	public void delete(final ID id) {
		AssertUtils.notNull(id, "id不能为空");
		delete(load(id));
	}
	
	/**
	 * 按id删除多个实体
	 */
	public void deleteMulti(ID[] ids){
		AssertUtils.notNull(ids, "ids不能为空");
		for (ID id : ids){
			delete(id);
		}
	}

	/**
	 * 按id获取代理对象.
	 */
	public T load(final ID id) {
		if (id == null) 
			return null;
		return (T) getSession().load(entityClass, id);
	}
	
	/**
	 * 按id获取非代理对象。
	 */
	public T get(ID id){
		if (id == null) return null;
		T t = (T)this.getSession().get(entityClass, id);
		getSession().evict(t);
		return t;
	}

	/**
	 * 按id列表获取对象列表.
	 */
	public List<T> get(final Collection<ID> ids) {
		if (ids == null || ids.isEmpty()) return null;
		return find(Restrictions.in(getIdName(), ids));
	}
	
	public List<T> get(ID[] ids) {
		if (ids == null || ids.length == 0) return null;
		return find(Restrictions.in(getIdName(), ids));
	}
	
	/**
	 *	获取全部对象.
	 */
	public List<T> getAll() {
		return find();
	}

	/**
	 *	获取全部对象, 可指定排序方式.
	 */
	public List<T> getAll(Order... orders) {
		Criteria c = createCriteria();
		for (Order order : orders){
			c.addOrder(order);
		}
		return c.list();
	}

	/**
	 * 按属性查找对象列表, 匹配方式为相等.
	 */
	public List<T> findBy(final String propertyName, final Object value) {
		AssertUtils.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = value != null ? Restrictions.eq(propertyName, value) : Restrictions.isNull(propertyName);
		return find(criterion);
	}

	/**
	 * 按属性查找唯一对象, 匹配方式为相等.
	 */
	public T findUniqueBy(final String propertyName, final Object value) {
		AssertUtils.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(criterion).uniqueResult();
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 */
	public <X> List<X> find(final String hql, final Object... values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按HQL查询对象列表.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public <X> List<X> find(final String hql, final Map<String, ?> values) {
		return createQuery(hql, values).list();
	}

	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 */
	public <X> X findUnique(final String hql, final Object... values) {
		return (X) createQuery(hql, values).uniqueResult();
	}

	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	public <X> X findUnique(final String hql, final Map<String, ?> values) {
		return (X) createQuery(hql, values).uniqueResult();
	}

	/**
	 * 执行HQL进行批量修改/删除操作.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 * @return 更新记录数.
	 */
	protected int batchExecute(final String hql, final Object... values) {
		return createQuery(hql, values).executeUpdate();
	}

	/**
	 * 执行HQL进行批量修改/删除操作.
	 * 
	 * @param values 命名参数,按名称绑定.
	 * @return 更新记录数.
	 */
	protected int batchExecute(final String hql, final Map<String, ?> values) {
		return createQuery(hql, values).executeUpdate();
	}

	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 与find()函数可进行更加灵活的操作.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 */
	protected Query createQuery(final String queryString, final Object... values) {
		AssertUtils.hasText(queryString, "queryString不能为空");
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query;
	}

	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 与find()函数可进行更加灵活的操作.
	 * 
	 * @param values 命名参数,按名称绑定.
	 */
	protected Query createQuery(final String queryString, final Map<String, ?> values) {
		AssertUtils.hasText(queryString, "queryString不能为空");
		Query query = getSession().createQuery(queryString);
		if (values != null) {
			query.setProperties(values);
		}
		return query;
	}

	/**
	 * 按Criteria查询对象列表.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	public List<T> find(final Criterion... criterions) {
		return createCriteria(criterions).list();
	}
	
	public List<T> find(Criterion criterion, Order order){
		Criteria criteria = getSession().createCriteria(entityClass);
		return criteria.add(criterion).addOrder(order).list();
	}

	public List<T> find(Criterion[] criterions, Order[] orders){
		Criteria criteria = getSession().createCriteria(entityClass);
		if (criterions != null){
			for (Criterion c : criterions){
				criteria.add(c);
			}
		}
		if (orders != null){
			for (Order order : orders){
				criteria.addOrder(order);
			}
		}
		return criteria.list();
	}
	
	public List<T> find(List<Criterion> criterions, Order... orders){
		Criteria criteria = getSession().createCriteria(entityClass);
		if (criterions != null){
			for (Criterion c : criterions){
				criteria.add(c);
			}
		}
		if (orders != null){
			for (Order order : orders){
				criteria.addOrder(order);
			}
		}
		return criteria.list();
	}
	
	/**
	 * 通过Example来查找，字符串可模糊查询，并支持多关键字
	 */
	public List<T> findByExample(T entity, Order order){
		if (entity != null){
			LangUtils.makeFieldsLike(entity);
		}
		return this.find(Example.create(entity).enableLike(), order);
	}
	
	/**
	 * 按Criteria查询唯一对象.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	public T findUnique(final Criterion... criterions) {
		return (T) createCriteria(criterions).uniqueResult();
	}

	/**
	 * 根据Criterion条件创建Criteria.
	 * 与find()函数可进行更加灵活的操作.
	 * 
	 * @param criterions 数量可变的Criterion.
	 */
	protected Criteria createCriteria(final Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}

	/**
	 * 初始化对象.
	 * 使用load()方法得到的仅是对象Proxy, 在传到View层前需要进行初始化.
	 * 如果传入entity, 则只初始化entity的直接属性,但不会初始化延迟加载的关联集合和属性.
	 * 如需初始化关联属性,需执行:
	 * Hibernate.initialize(user.getRoles())，初始化User的直接属性和关联集合.
	 * Hibernate.initialize(user.getDescription())，初始化User的直接属性和延迟加载的Description属性.
	 */
	public void initProxyObject(Object proxy) {
		Hibernate.initialize(proxy);
	}

	/**
	 * Flush当前Session.
	 */
	public void flush() {
		getSession().flush();
	}

	/**
	 * 为Query添加distinct transformer.
	 * 预加载关联对象的HQL会引起主对象重复, 需要进行distinct处理.
	 */
	public Query distinct(Query query) {
		query.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return query;
	}

	/**
	 * 为Criteria添加distinct transformer.
	 * 预加载关联对象的HQL会引起主对象重复, 需要进行distinct处理.
	 */
	public Criteria distinct(Criteria criteria) {
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		return criteria;
	}

	/**
	 * 取得对象的主键名.
	 */
	public String getIdName() {
		ClassMetadata meta = sessionFactory.getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}

	/**
	 * 判断对象的属性值在数据库内是否唯一.
	 * 
	 * 在修改对象的情景下,如果属性新修改的值(value)等于属性原来的值(orgValue)则不作比较.
	 */
	public boolean isPropertyUnique(final String propertyName, final Object newValue, final Object oldValue) {
		if (newValue == null || newValue.equals(oldValue)) {
			return true;
		}
		Object object = findUniqueBy(propertyName, newValue);
		return (object == null);
	}
	
	/**
	 * 判断即将要保存到数据库的实体是否唯一，忽略null值
	 * @param entity 要保存的实体
	 * @param props 确保要唯一的属性组合
	 */
	public boolean isPropsUnique(T entity, String... props){
		AssertUtils.notNull(entity);
		AssertUtils.notEmpty(props);
		//根据属性组合查找已保存的记录
		List<Criterion> criterions = new ArrayList<Criterion>();
		for (String prop : props){
			Object value = ReflectionUtils.getFieldValue(entity, prop);
			criterions.add(Restrictions.eq(prop, value));
		}
		List<T> list = this.find(criterions);
		
		Long newId = ((IdEntity)entity).getId();
		//如果是新增实体的情况，已保存的记录为空则表示唯一
		if (newId == null){
			return list.isEmpty();
		}
		//如果是修改实体的情况，已保存的记录为空或者刚好是要修改的实体则表示唯一
		else if (list.isEmpty()){
			return true;
		}
		else{
			Long oldId =  ((IdEntity)list.get(0)).getId();
			if (oldId.equals(newId)){
				getSession().evict(list.get(0)); // 清除缓存，否则影响保存
				return true;
			}
			return false;
		}
	}
	
	/**
	 * 级联删除实体，返回所有被删除实体的id
	 */
	protected Set<ID> deleteCascade(T entity, String parentName){
		Set<ID> ids = new HashSet<ID>();
		Stack<T> stack = new Stack<T>();
		stack.push(entity);
		while(!stack.empty()){
			T t = stack.pop();
			ID id = (ID)getSession().getIdentifier(t);
			stack.addAll(find(Restrictions.eq(parentName, id))); //子对象入栈
			try{
				getSession().delete(t);
				ids.add(id);
			}
			catch(ObjectNotFoundException obj){
			}
		}
		return ids;
	}
	
	/**
	 * 级联删除实体，返回所有被删除实体的id
	 */
	protected Set<ID> deleteCascade(ID id, String parentName){
		try{
			T e = load(id);
			return deleteCascade(e, parentName);
		}
		catch(ObjectNotFoundException obj){
		}
		return new HashSet<ID>(0);
	}

	/**
	 * 判断指定字段名和字段值的记录是否存在
	 */
	protected boolean exists(String fieldName, Object value){
		String hql = "from " + this.entityClass.getName() + " where " + fieldName + "=?";
		return this.createQuery(hql, value).setMaxResults(1).iterate().hasNext();
	}
	
	/**
	 * 删除当前session的实体
	 */
	public void evict(Object entity){
		this.getSession().evict(entity);
	}
}